name: CI
on:
  push:
    branches:
      - master
  pull_request:

# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
  group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
  cancel-in-progress: true

jobs:
  setup:
    name: Prepare workflow data
    runs-on: ubuntu-latest
    timeout-minutes: 2
    outputs:
      config-path: ${{ steps.load-config.outputs.config-path }}
      device-list: ${{ steps.load-config.outputs.device-list }}
      magisk-key: ${{ steps.cache-keys.outputs.magisk-key }}
      img-key-prefix: ${{ steps.cache-keys.outputs.img-key-prefix }}
      img-hit: ${{ steps.get-img-cache.outputs.cache-matched-key }}
      tox-key-prefix: ${{ steps.cache-keys.outputs.tox-key-prefix }}
      tox-hit: ${{ steps.get-tox-cache.outputs.cache-matched-key }}
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: true

      - uses: awalsh128/cache-apt-pkgs-action@v1
        with:
          packages: python3-strictyaml

      - name: Loading test config
        id: load-config
        shell: python
        run: |
          import json
          import os
          import sys

          sys.path.append(os.environ['GITHUB_WORKSPACE'])
          import tests.config

          config_data = tests.config.load_config()
          devices = [d.data for d in config_data['device']]

          with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
              f.write(f'config-path={tests.config.CONFIG_PATH}\n')
              f.write(f"device-list={json.dumps(devices)}\n")

      - name: Generating cache keys
        id: cache-keys
        run: |
          {
            echo "tox-key-prefix=tox-${{ hashFiles('tox.ini') }}-"; \
            echo "img-key-prefix=img-${{ hashFiles(steps.load-config.outputs.config-path) }}-"; \
            echo "magisk-key=magisk-${{ hashFiles(steps.load-config.outputs.config-path) }}";
          } >> $GITHUB_OUTPUT

      - name: Checking for cached tox environments
        id: get-tox-cache
        uses: actions/cache/restore@v3
        with:
          key: ${{ steps.cache-keys.outputs.tox-key-prefix }}
          lookup-only: true
          path: |
            .tox/
            ~/.cache/pip

      - name: Checking for cached device images
        id: get-img-cache
        uses: actions/cache/restore@v3
        with:
          key: ${{ steps.cache-keys.outputs.img-key-prefix }}
          lookup-only: true
          path: |
            tests/files/${{ fromJSON(steps.load-config.outputs.device-list)[0] }}-sparse.tar

      - name: Checking for cached magisk apk
        id: get-magisk-cache
        uses: actions/cache/restore@v3
        with:
          key: ${{ steps.cache-keys.outputs.magisk-key }}
          lookup-only: true
          path: tests/files/magisk

      - name: Preloading Magisk cache
        if: ${{ ! steps.get-magisk-cache.outputs.cache-hit }}
        uses: ./.github/actions/preload-magisk-cache
        with:
          cache-key: ${{ steps.cache-keys.outputs.magisk-key }}

  preload-img:
    name: Preload device images
    runs-on: ubuntu-latest
    needs: setup
    timeout-minutes: 5
    # Assume that preloading always succesfully cached all images before.
    # If for some reason only some got cached, on the first run, the cache will not be preloaded
    # which will result in some being downloaded multiple times when running the tests.
    if: ${{ ! needs.setup.outputs.img-hit }}
    strategy:
      matrix:
        device: ${{ fromJSON(needs.setup.outputs.device-list) }}
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: true

      - name: Preloading image cache
        uses: ./.github/actions/preload-img-cache
        with:
          cache-key-prefix: ${{ needs.setup.outputs.img-key-prefix }}
          device: ${{ matrix.device }}

  preload-tox:
    name: Preload tox environments
    runs-on: ubuntu-latest
    needs: setup
    timeout-minutes: 5
    # Assume that preloading always succesfully cached all tox environments before.
    # If for some reason only some got cached, on the first run, the cache will not be preloaded
    # which will result in some being downloaded multiple times when running the tests.
    if: ${{ ! needs.setup.outputs.tox-hit }}
    strategy:
      matrix:
        python: [py39, py310, py311]
    steps:
      - uses: actions/checkout@v3

      - name: Preloading tox cache
        uses: ./.github/actions/preload-tox-cache
        with:
          cache-key-prefix: ${{ needs.setup.outputs.tox-key-prefix }}
          python-version: ${{ matrix.python }}

      - name: Generating tox environment
        run: tox -e ${{ matrix.python }} --notest

  tests:
    name: Run test for ${{ matrix.device }} with ${{ matrix.python }}
    runs-on: ubuntu-latest
    needs: [setup, preload-img, preload-tox]
    timeout-minutes: 10
    # Continue on skipped but not on failures or cancels
    if: ${{ always() && ! failure() && ! cancelled() }}
    strategy:
      matrix:
        device: ${{ fromJSON(needs.setup.outputs.device-list) }}
        python: [py39, py310, py311]
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: true

      - name: Restoring Magisk cache
        uses: ./.github/actions/preload-magisk-cache
        with:
          cache-key: ${{ needs.setup.outputs.magisk-key }}

      - name: Restoring image cache
        uses: ./.github/actions/preload-img-cache
        with:
          cache-key-prefix: ${{ needs.setup.outputs.img-key-prefix }}
          device: ${{ matrix.device }}

      - name: Restoring tox cache
        uses: ./.github/actions/preload-tox-cache
        with:
          cache-key-prefix: ${{ needs.setup.outputs.tox-key-prefix }}
          python-version: ${{ matrix.python }}

      # Finally run tests
      - name: Run test for ${{ matrix.device }} with ${{ matrix.python }}
        run: tox -e ${{ matrix.python }} -- --stripped -d ${{ matrix.device }}
